package org.muninn.niagara.services;

import javax.baja.collection.BIList;
import javax.baja.control.BControlPoint;
import javax.baja.control.BNumericWritable;
import javax.baja.naming.BOrd;
import javax.baja.sys.Action;
import javax.baja.sys.BDouble;
import javax.baja.sys.BIcon;
import javax.baja.sys.BRelTime;
import javax.baja.sys.Clock;
import javax.baja.sys.Flags;
import javax.baja.sys.Property;
import javax.baja.sys.Sys;
import javax.baja.sys.Type;

public class BCyclerService extends BMuninnService
{
  /** Enable for the service **/
  public static final Property enabled = newProperty(0, false);
  public void setEnabled(boolean v) { setBoolean(enabled, v); }
  public boolean getEnabled() { return getBoolean(enabled); }
  
  /** Time interval at which object executes BQL query and calculates values */
  public static final Property executePeriod = newProperty(0,  BRelTime.make(604800000));
  public void setExecutePeriod(BRelTime v) { set(executePeriod, v); }
  public BRelTime getExecutePeriod() { return (BRelTime) get(executePeriod); }
  
  /** Path to the location of the point*/
  public static final Property pointPath = newProperty(0, BOrd.make("station:|slot:/Drivers"));
  public void setPointPath(BOrd v) { set(pointPath, v); }
  public BOrd getPointPath() { return (BOrd) get(pointPath); }
  
  /** Name of the point to be synced */
  public static final Property pointName = newProperty(0, "");
  public void setPointName(String v) { setString(pointName, v, null); }
  public String getPointName() { return getString(pointName); }
  
  /** Min position */
  public static final Property min = newProperty(0, 0);
  public void setMin(double v) { setDouble(min, v, null); }
  public double getMin() { return getDouble(min); }
  
  /** Max position */
  public static final Property max = newProperty(0, 100);
  public void setMax(double v) { setDouble(max, v, null); }
  public double getMax() { return getDouble(max); }
  
  /** Delay time in minutes between cycle stages */
  public static final Property delay = newProperty(0, BRelTime.make(300000));
  public void setDelay(BRelTime v) { set(delay, v); }
  public BRelTime getDelay() { return (BRelTime) get(delay); }
  
  /** Action to manually invoke a cycle (ignores <code>enabled</code>) **/
  public static final Action performCycle = newAction(0, null);
  public void performCycle() { invoke(performCycle, null, null); }
  public void doPerformCycle() throws Exception { cycle(); }
  
  /** Action invoked by timer going off (depends on <code>enabled</code>) **/
  public static final Action timerExpired = newAction(Flags.HIDDEN, null);
  public void timerExpired() { invoke(timerExpired, null, null); }
  public void doTimerExpired() throws Exception { cyclePeriodic(); }
  
  private static BCyclerService INSTANCE = null;
  
  private static String path;
  private static String point;
  private static double minimum;
  private static double maximum;
  private BControlPoint[] points;
  
  public void started() { super.started(); updateTimer(); }
  
  public void cyclePeriodic() throws InterruptedException { if (getEnabled()) cycle(); }
  
  public void cycle() throws InterruptedException
  {
    
    path = getPointPath().toString();
    point = getPointName();
    minimum = getMin();
    maximum = getMax();

    BIList rawPoints;
    rawPoints = (BIList) BOrd.make(path + "|bql:select * from control:ControlPoint where displayName = '" + point + "'").resolve().get();
    
    if (rawPoints.size() == -1) return;
    
    points = new BControlPoint[rawPoints.size()];
    points = (BControlPoint[]) rawPoints.list(points);
    
    for (int i = 0; i < points.length; i++)
    {
      BNumericWritable temp = (BNumericWritable) points[i];
      temp.emergencyOverride(BDouble.make(maximum));
    }
    
    long time = Clock.millis();
    while ((Clock.millis() - time) < getDelay().getMillis()) {}
    
    for (int i = 0; i < points.length; i++)
    {
      BNumericWritable temp = (BNumericWritable) points[i];
      temp.emergencyOverride(BDouble.make(minimum));
    }
    
    time = Clock.millis();
    while ((Clock.millis() - time) < getDelay().getMillis()) {}
    
    for (int i = 0; i < points.length; i++)
    {
      BNumericWritable temp = (BNumericWritable) points[i];
      temp.emergencyAuto();
    }
  }

  Clock.Ticket ticket;
  long lastOnExecuteTicks;
  
  public void stopped() throws Exception { if (ticket != null) ticket.cancel(); }
  
  void updateTimer()
  {            
    if (ticket != null) ticket.cancel();
    ticket = Clock.schedulePeriodically(this, getExecutePeriod(), timerExpired, null);
  }
  
  public BIcon getIcon() { return icon; }
  private static final BIcon icon = BIcon.make("local:|module://muninn/org/muninn/niagara/graphics/MuninnGroup.PNG");
  
  public static final Type TYPE = Sys.loadType(BCyclerService.class);
  public Type[] getServiceTypes() { return new Type[] { TYPE }; }
  public Type getType() { return TYPE; }

  public final synchronized void serviceStarted() throws Exception { INSTANCE = this; }

  public final synchronized void serviceStopped() throws Exception { INSTANCE = null; }
  
  public static final BCyclerService getInstance() { return INSTANCE; }
}
